package server.servlet;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import json.JSONObjectWrapper;

import org.apache.log4j.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;

import server.database.DatabaseColumn;
import server.database.IDatabaseTable;
import utils.StringUtils;

import com.google.common.collect.Maps;

public abstract class AbstractServlet extends HttpServlet
{
	private static final Logger log = Logger.getLogger(AbstractServlet.class);
	private static final long serialVersionUID = -5278164562912568964L;

	private static final String CONTENT_TYPE = "text/html";
	protected static final String API = "/api";

	public static final String UNEXPECTED_ERROR = "unexpected error";
	public static final String UNKNOWN_TYPE = "Unknown request type ";
	public static final String PARSE_ERROR = "Error parsing POST request";

	public static final String RESULT = "success";
	public static final String QUERY_RESULT = "query_result";
	public static final String ERROR = "error";
	public static final String LOG_ERROR = "log_error";

	public static final String REQUEST_TYPE = "request_type";
	public static final String ADD = "add";
	public static final String UPDATE = "update";
	public static final String DELETE = "delete";

	protected abstract void doGetSpecific(HttpServletRequest request, HttpServletResponse response, JSONObject output) throws ServletException, IOException;
	protected abstract void doPostSpecific(JSONObjectWrapper request, JSONObject output) throws ServletException, IOException;
	protected abstract IDatabaseTable getTable();

	protected JSONObject setFail(JSONObject output, String sError, String sLogError)
	{
		output.put(RESULT, false);
		output.put(ERROR, sError);
		output.put(LOG_ERROR, sLogError);
		return output;
	}

	@Override
	protected final void doGet(HttpServletRequest req, HttpServletResponse resp)
	{
		resp.setContentType(CONTENT_TYPE);
		resp.setStatus(HttpServletResponse.SC_OK);

		try
		{
			JSONObject output = new JSONObject();
			doGetSpecific(req, resp, output);
			resp.getWriter().print(output);

		}
		catch (ServletException | IOException e)
		{
			JSONObject output = new JSONObject();
			setFail(output, UNEXPECTED_ERROR, e.getMessage());
			log.error("doGet() error - " + e.getMessage());
		}
	}

	@Override
	protected final void doPost(HttpServletRequest req, HttpServletResponse resp)
	{
		resp.setContentType(CONTENT_TYPE);
		resp.setStatus(HttpServletResponse.SC_OK);

		try
		{
			JSONObjectWrapper wrapper = new JSONObjectWrapper(req.getReader().readLine());

			JSONObject output = new JSONObject();
			doPostSpecific(wrapper, output);
			resp.getWriter().print(output);

		}
		catch (ServletException | IOException | ParseException e)
		{
			setFail(new JSONObject(), UNEXPECTED_ERROR, e.getMessage());
			log.error("doPost() error - " + e.getMessage());
		}
	}

	protected void buildEntityValues(List<Map<String, String>> lstQueryResultValues, JSONArray entities, IDatabaseTable table)
	{
		for (Map<String, String> entity : lstQueryResultValues)
		{
			JSONObject entityObject = buildEntityValue(entity, table);
			entities.add(entityObject);
		}
	}

	protected JSONObject buildEntityValue(Map<String, String> queryResultValue, IDatabaseTable table)
	{
		JSONObject entityObject = new JSONObject();
		for (DatabaseColumn column : table.getColumns())
		{
			String sValue = queryResultValue.get(column.getName());
			if ((!StringUtils.isEmpty_DB(sValue)) && (!column.isHidden()))
			{
				entityObject.put(column.getName(), sValue);
			}
		}

		return entityObject;
	}

	protected Map<DatabaseColumn, String> parseEntityValues(JSONObjectWrapper entity, IDatabaseTable table)
	{
		Map<DatabaseColumn, String> mpValues = Maps.newHashMap();

		List<DatabaseColumn> lstColumns = table.getColumns();

		for (DatabaseColumn column : lstColumns)
		{
			String sValue = entity.getStringValue(column.getName());
			if (!StringUtils.isEmpty_DB(sValue))
			{
				mpValues.put(column, sValue);
			}
		}

		return mpValues;
	}
}
